#!/bin/bash
################################################################################
# Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The OpenAirInterface Software Alliance licenses this file to You under 
# the Apache License, Version 2.0  (the "License"); you may not use this file
# except in compliance with the License.  
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-------------------------------------------------------------------------------
# For more information about the OpenAirInterface (OAI) Software Alliance:
#      contact@openairinterface.org
################################################################################

# file run_spgw
# brief run script for S/P-GW (S-GW + P-GW).
# author  Lionel GAUTHIER
# company Eurecom
# email:  lionel.gauthier@eurecom.fr 


################################
# include helper functions
################################
THIS_SCRIPT_PATH=$(dirname $(readlink -f $0))
source $THIS_SCRIPT_PATH/../build/tools/build_helper.ovs

declare -i g_run_msc_gen=0
declare    g_msc_dir="/tmp"
declare    g_spgw_default_config_file="/usr/local/etc/oai/spgw.conf"

LIBGTPNL="LIBGTPNL"
LIBGTPNL_OVS="LIBGTPNL_OVS"
OPENFLOW_MOSAIC="OPENFLOW_MOSAIC"
OPENFLOW="OPENFLOW"
REST="REST"
GTPU_API=$OPENFLOW
SGI_DUMMY_MAC_NH="00.11.22.33.44.55"

OVS_GTPU_BRIDGE_NAME="spgwu"
CTRL_IP="127.0.0.1"
CTRL_PORT="6654"
UDP_PORT_FOR_S1U="2152"

set_openair_env 


function help()
{
  echo_error " "
  echo_error "Usage: run_spgw [OPTION]..."
  echo_error "Run the S/G-GW executable (S-GW + P-GW)."
  echo_error " "
  echo_error "Options:"
  echo_error "Mandatory arguments to long options are mandatory for short options too."
  echo_error "  -c, --config-file     file_abs_path Config file to be used by spgw if you don't want to use the default one: $g_spgw_default_config_file"
  echo_error "  -d, --daemon                        Run spgw as a daemon."
  echo_error "  -g, --gdb                           Run with GDB."
  echo_error "  -G, --gdb-cmd         cmd cmd_arg   Append this GDB cmd to GDB command file (ex1: break Attach.c:272, ex2: watch 0xffee0002)."
  echo_error "                                      All repetitions of this argument are valid."
  echo_error "  --gtpu       api                    GTPV1-U implementation, choice in [$LIBGTPNL, $OPENFLOW_MOSAIC, $OPENFLOW], default is $GTPU_API"
  echo_error "  -h, --help                          Print this help."
  echo_error "  -k, --kill                          Kill all running SPGW instances."
  echo_error "  -K, --Kill                          Kill all running SPGW instances, exit script then."
  echo_error "  -m, --mscgen          directory     Generate mscgen output files in a directory"
  echo_error "  --sgi-nh         nh                 SGi next-hop IP address (can be a gateway, router, single app server), will be arp'ed for GTP data "
  echo_error "                                      plane if OVS is used, default is SGi network a.b.c.1 if no --sgi-mac-nh supplied."
  echo_error "  --sgi-dum-mac-nh                    Dummy SGi next-hop L2 address for GTP data plane if OVS is used"
  echo_error "  --sgi-mac-nh     nh                 SGi next-hop L2 address (can be a gateway, router, single app server) for GTP data plane if OVS is used"
  echo_error "                                      --sgi-nh and --sgi-mac-nh and --sgi-dum-mac-nh are mutually exclusive if OVS is used, else don't use them."
  echo_error "  -s, --set-virt-if                   Set virtual interfaces if any listed in config file (default or provided with -c option)"
}

function do_msc_gen()
{
  cd $g_msc_dir
  $THIS_SCRIPT_PATH/msc_gen
}

function control_c()
# run if user hits control-c
{
  echo_warning "\nExiting by ctrl+c\n"
  if [ $g_run_msc_gen -eq 1 ]; then 
    do_msc_gen
  fi
  exit $?
}

PIDFILE=/var/run/$NAME.pid
function main()
{
  local -i run_gdb=0
  local -i run_daemon=0
  local -i set_virt_nw_if=0
  local    exe_arguments=" "
  local    spgw_config_file=$g_spgw_default_config_file
  local    sgi_nh
  local    sgi_mac_nh
  local    breakpoint_location=""
  local -a gdb_args1
  local -a gdb_args2
  local -i gdb_index=0

  until [ -z "$1" ]
    do
    case "$1" in
      -c | --config-file)
        spgw_config_file=$2
        echo "setting config file to: $spgw_config_file"
        shift 2;
        ;;
      -d | --daemon)
        run_daemon=1
        shift;
        ;;
      -g | --gdb)
        run_gdb=1
        echo "setting GDB flag to: $run_gdb"
        shift;
        ;;
      -G | --gdb-arg)
        run_gdb=1
        gdb_args1[$gdb_index]="$2"
        gdb_args2[$gdb_index]="$3"
        echo "Appending gdb args: ${gdb_args1[$gdb_index]} ${gdb_args2[$gdb_index]}"
        gdb_index=$((gdb_index + 1))
        shift 3;
        ;;
      --gtpu)
        list_include_item "$LIBGTPNL $OPENFLOW $OPENFLOW_MOSAIC" $2
        ret=$?;[[ $ret -ne 0 ]] && echo_error "GTPV1U API type $2 not recognized or not available" && return $ret
        GTPU_API=$2
        shift 2;
        ;;
      -h | --help)
        help
        return 0
        ;;
      -k | --kill)
        $SUDO killall -q spgw
        $SUDO rm /var/run/spgw*.pid
        ovs_stop $OVS_GTPU_BRIDGE_NAME
        shift;
        ;;
      -K | --Kill)
        $SUDO killall -q spgw
        $SUDO rm /var/run/spgw*.pid
        ovs_stop $OVS_GTPU_BRIDGE_NAME
        return 0
        shift;
        ;;
      -m | --mscgen)
        g_msc_dir=$2
        if [ -d  "$g_msc_dir" ]; then
          echo "setting mscgen log files to dir: $g_msc_dir"
          g_run_msc_gen=1
          shift 2;
        else
          echo_error "Mscgen log dir does not exist"
          return -1
        fi
        ;;      
      --sgi-dum-mac-nh)
        sgi_mac_nh=$SGI_DUMMY_MAC_NH
        shift;
        ;;
      --sgi-mac-nh)
        sgi_mac_nh=$2
        shift 2;
        ;;
      --sgi-nh)
        sgi_nh=$2
        shift 2;
        ;;
      -s | --set-virt-if)
        set_virt_nw_if=1
        shift;
        ;;
      *)   
        echo "Unknown option $1"
        help
        return 1
        ;;
    esac
  done

  set_openair_env 
  cecho "OPENAIR_DIR    = $OPENAIR_DIR" $green
  
  export ENABLE_"$GTPU_API"=1

  if [ ! -f $spgw_config_file ]; then 
    echo_error "Please provide -c|--config-file valid argument (\"$spgw_config_file\" not a valid file)"
    return 1
  fi

  

  if [ ! -e /usr/local/bin/spgw ]; then
    echo_error "Cannot find /usr/local/bin/spgw executable, have a look at the output of build_spgw executable"
    return 1
  fi


  if [ $g_run_msc_gen -eq 1 ]; then 
    rm -f /tmp/openair.msc.*
  fi


  if [ $set_virt_nw_if -eq 1 ]; then 
    set_cn_virtual_interfaces $spgw_config_file
    ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to set virtual interfaces" && return $ret
  fi
  


  ##########################
  # INSTANCE
  ##########################
  instance_string="INSTANCE"
  instance="`cat $spgw_config_file | cut -d "#" -f1 | grep $instance_string | tr -d " " | grep "="`"
  eval $instance
  INSTANCE=${!instance_string}

  OVS_GTPU_BRIDGE_NAME=$OVS_GTPU_BRIDGE_NAME${!instance_string}
  echo_info OVS_GTPU_BRIDGE_NAME=$OVS_GTPU_BRIDGE_NAME


  ##########################
  # SGI INTERFACE
  ##########################
  sgi_nw_dev_string="PGW_INTERFACE_NAME_FOR_SGI"
  sgi_nw_device="`cat $spgw_config_file | cut -d "#" -f1 | grep $sgi_nw_dev_string | tr -d " " | grep "="`"
  eval $sgi_nw_device
  is_interface_up ${!sgi_nw_dev_string}
  ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to find SGi interface ${!sgi_nw_dev_string} up" && return $ret

  sgi_ip_cidr_string="PGW_IPV4_ADDRESS_FOR_SGI"
  sgi_ip_cidr="`cat $spgw_config_file | cut -d "#" -f1 | grep $sgi_ip_cidr_string | tr -d " " | grep "="`"
  eval $sgi_ip_cidr
  #sgi_ip_cidr=$(get_interface_inet_global_cidr ${!sgi_nw_dev_string})
  sgi_ip=$(echo $sgi_ip_cidr | cut -d '/' -f1)

  echo_success "SGI interface ${!sgi_nw_dev_string} found inet ${!sgi_ip_cidr_string} in config file"

  #echo_info "Setting in $spgw_config_file PGW_IPV4_ADDRESS_FOR_SGI = $sgi_ip_cidr"
  #sed -i "s|.*PGW_IPV4_ADDRESS_FOR_SGI.*|        PGW_IPV4_ADDRESS_FOR_SGI = \"$sgi_ip_cidr\";|" $spgw_config_file

  ##########################
  # GTPV1-U INTERFACE
  # By default we assign the S1U IP address to the GTPU bridge
  ##########################
  s1u_nw_dev_string="SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP"
  s1u_nw_device="`cat $spgw_config_file | cut -d "#" -f1 | grep $s1u_nw_dev_string | tr -d " " | grep "="`"
  eval $s1u_nw_device
  is_interface_up ${!s1u_nw_dev_string}
  ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to find S1-U interface up" && return $ret
  s1u_ip_cidr=$(get_interface_inet_global_cidr ${!s1u_nw_dev_string})
  s1u_ip=$(echo $s1u_ip_cidr | cut -d '/' -f1)
  echo_success "S1-U interface ${!s1u_nw_dev_string} found inet $s1u_ip_cidr in config file"

  echo_info "Setting in $spgw_config_file SGW_IPV4_ADDRESS_FOR_S1U_S12_S4_UP = $s1u_ip_cidr"
  sed -i "s|.*SGW_IPV4_ADDRESS_FOR_S1U_S12_S4_UP.*|        SGW_IPV4_ADDRESS_FOR_S1U_S12_S4_UP = \"$s1u_ip_cidr\";|" $spgw_config_file


  if [ -z ${ENABLE_OPENFLOW+x} ]  ||  [ -z ${ENABLE_OPENFLOW_MOSAIC+x} ]; then
    ovs_start
    ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to start OVS" && return $ret

    sleep 1
    $SUDO ovs-vsctl del-br $OVS_GTPU_BRIDGE_NAME
    sleep 1

    if [ -z $sgi_mac_nh ]; then
    
      $SUDO ifconfig ${!sgi_nw_dev_string} ${!sgi_ip_cidr_string}
      
      if [ -z $sgi_nh ]; then
        subnet=$(ip address show dev ${!sgi_nw_dev_string} | grep inet | grep -v inet6 | xargs | cut -d ' ' -f2 | cut -d '/' -f1 | cut -d '.' -f1-3)
        [[ $? -ne 0 ]] && echo_error "Failed to get subnet of SGi interface" && return $?
        sgi_nh=$subnet'.1'
        echo "Set SGi next hop to $sgi_nh"
      fi
      # do arp sgi_nh
      echo "@0" ${!sgi_nw_dev_string}
      ping -I ${!sgi_nw_dev_string} -c 1 $sgi_nh
      # May be configured to not reply to ping
      # Assume default route is through SGi
      ret=$?
      if [[ $ret -ne 0 ]]; then 
        ping -I ${!sgi_nw_dev_string} -c 1 www.google.com
        ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to ping google server after failed to ping SGI next hop" && return $ret
      fi
      sgi_mac_nh=$(arp --device ${!sgi_nw_dev_string} --numeric | grep $sgi_nh | xargs | cut -d ' ' -f3)
      ret=$?;[[ $ret -ne 0 ]] && echo_error "Failed to get L2 address of SGi next hop" && return $ret
    fi
    echo "Set SGi next hop L2 address to $sgi_mac_nh"


    ##########################
    # Bridge configuration
    ##########################
    #$SUDO ovs-vsctl add-br $OVS_GTPU_BRIDGE_NAME -- set bridge $OVS_GTPU_BRIDGE_NAME protocols=OpenFlow10,OpenFlow13 
    $SUDO ovs-vsctl add-br $OVS_GTPU_BRIDGE_NAME
    ret=$?;[[ $ret -ne 0 ]] && echo_error "Could not create OVS switch $OVS_GTPU_BRIDGE_NAME" && return $ret
    echo_success "Created switch $OVS_GTPU_BRIDGE_NAME"
    sleep 1
    
    echo "Add port ${!sgi_nw_dev_string} to $OVS_GTPU_BRIDGE_NAME"
    $SUDO ovs-vsctl add-port $OVS_GTPU_BRIDGE_NAME ${!sgi_nw_dev_string}
    ret=$?;[[ $ret -ne 0 ]] && echo_error "Could not add port ${!sgi_nw_dev_string} to OVS switch $OVS_GTPU_BRIDGE_NAME" && return $ret
    sleep 1
    
    S1U_PORT='s1u'
    echo "Add port $S1U_PORT to $OVS_GTPU_BRIDGE_NAME"
    $SUDO ovs-vsctl add-port $OVS_GTPU_BRIDGE_NAME $S1U_PORT -- set Interface $S1U_PORT type=gtp options:remote_ip=flow -- set Interface $S1U_PORT type=gtp options:key=flow
    ret=$?;[[ $ret -ne 0 ]] && echo_error "Could not add port $S1U_PORT to OVS switch $OVS_GTPU_BRIDGE_NAME" && return $ret
    
    echo "ifconfig $OVS_GTPU_BRIDGE_NAME up"
    $SUDO ifconfig $OVS_GTPU_BRIDGE_NAME up
    #ret=$?;[[ $ret -ne 0 ]] && return $ret
    sleep 1
    
    echo "ifconfig  ${!sgi_nw_dev_string} 0"
    $SUDO ifconfig  ${!sgi_nw_dev_string} 0
    ret=$?;[[ $ret -ne 0 ]] && return $ret
    sleep 1

    echo "ip addr add ${!sgi_ip_cidr_string} dev $OVS_GTPU_BRIDGE_NAME"
    $SUDO ip addr add ${!sgi_ip_cidr_string} dev $OVS_GTPU_BRIDGE_NAME 
    ret=$?;[[ $ret -ne 0 ]] && return $ret
    
    $SUDO ovs-vsctl set-controller $OVS_GTPU_BRIDGE_NAME  tcp:$CTRL_IP:$CTRL_PORT
    ret=$?;[[ $ret -ne 0 ]] && return $ret


    # Port for GTPV1-U network device
    gtpu_port=$($SUDO ovs-ofctl dump-ports-desc $OVS_GTPU_BRIDGE_NAME | grep $S1U_PORT | cut -d '(' -f1)
    gtpu_mac=$($SUDO ovs-ofctl dump-ports-desc $OVS_GTPU_BRIDGE_NAME | grep $S1U_PORT | cut -d ':' -f3-8)
    echo "Found GTPU port switch to $gtpu_port L2 $gtpu_mac"

    sgi_port=$($SUDO ovs-ofctl dump-ports-desc $OVS_GTPU_BRIDGE_NAME | grep ${!sgi_nw_dev_string} | cut -d '(' -f1)
    sgi_mac=$($SUDO ovs-ofctl dump-ports-desc $OVS_GTPU_BRIDGE_NAME | grep ${!sgi_nw_dev_string} | cut -d ':' -f3-8)
    echo "Found SGi port switch to $sgi_port L2 $sgi_mac"

    # sed config file
    echo_info "Setting in $spgw_config_file BRIDGE_NAME = $OVS_GTPU_BRIDGE_NAME"
    sed -i "s/.*BRIDGE_NAME.*/        BRIDGE_NAME = \"$OVS_GTPU_BRIDGE_NAME\";/" $spgw_config_file
    echo_info "Setting in $spgw_config_file L2_EGRESS_PORT = $sgi_mac"
    sed -i "s/.*L2_EGRESS_PORT.*/        L2_EGRESS_PORT = \"$sgi_mac\";/" $spgw_config_file
    echo_info "Setting in $spgw_config_file EGRESS_PORT_NUM = $sgi_port"
    sed -i "s/.*EGRESS_PORT_NUM.*/        EGRESS_PORT_NUM = $sgi_port;/" $spgw_config_file
    echo_info "Setting in $spgw_config_file GTP_PORT_NUM = $gtpu_port"
    sed -i "s/.*GTP_PORT_NUM.*/        GTP_PORT_NUM = $gtpu_port;/" $spgw_config_file
    echo_info "Setting in $spgw_config_file UPLINK_MAC = $sgi_mac_nh"
    sed -i "s/.*UPLINK_MAC.*/        UPLINK_MAC = \"$sgi_mac_nh\";/" $spgw_config_file
    echo_info "Setting in $spgw_config_file UDP_PORT_FOR_S1U = $UDP_PORT_FOR_S1U"
    sed -i "s/.*UDP_PORT_FOR_S1U.*/        UDP_PORT_FOR_S1U = $UDP_PORT_FOR_S1U;/" $spgw_config_file
  fi

  exe_arguments="-c $spgw_config_file $exe_arguments"
  
  if [ $run_daemon -eq 1 ]; then 
    $SUDO daemon --env="CONFIG_FILE=$spgw_config_file" --name=upnf$INSTANCE --stdout=/tmp/spgwd"$INSTANCE"_stdout.log --stderr=/tmp/spgwd"$INSTANCE"_stderr.log --pidfile=/tmp/spgwd"$INSTANCE".pid -X /usr/local/bin/spgw
    ret=$?
    [[ $ret -ne 0 ]] && return $ret
    if [ $run_gdb -ne 0 ]; then 
      # trap keyboard interrupt (control-c) is done by gdb
      $SUDO touch      .gdb_spgwd
      $SUDO chmod 777  .gdb_spgwd
      
      $SUDO echo "set target-async on" > .gdb_spgwd
      $SUDO echo "set pagination off" >> .gdb_spgwd
      spgwd_pid=`pidof -s spgw`
      $SUDO echo "attach $spgwd_pid"  >> .gdb_spgwd
      for i in ${!gdb_args1[@]}; do
        $SUDO echo "${gdb_args1[i]} ${gdb_args2[i]}" >> .gdb_spgwd
      done
      $SUDO echo "continue"    >> .gdb_spgwd
      $SUDO cat .gdb_spgwd
      $SUDO gdb -n -x .gdb_spgwd
    fi
  else
    if [ $run_gdb -eq 0 ]; then 
      # trap keyboard interrupt (control-c)
      trap control_c SIGINT
      $SUDO spgw  `echo $exe_arguments` 2>&1 
    else
      # trap keyboard interrupt (control-c) is done by gdb
      $SUDO touch      ~/.gdb_spgw
      $SUDO chmod 777  ~/.gdb_spgw
      $SUDO echo "file spgw" > ~/.gdb_spgw
      $SUDO echo "set args $exe_arguments "        >> ~/.gdb_spgw
      for i in ${!gdb_args1[@]}; do
        $SUDO echo "${gdb_args1[i]} ${gdb_args2[i]}" >> .gdb_spgw
      done
      $SUDO echo "run"                             >> ~/.gdb_spgw
      $SUDO cat ~/.gdb_spgw
      $SUDO gdb -n -x ~/.gdb_spgw
      if [ $g_run_msc_gen -eq 1 ]; then 
        #$SUDO do_msc_gen
        cd $g_msc_dir
        $SUDO $THIS_SCRIPT_PATH/msc_gen --profile EPC --dir $g_msc_dir --type png
      fi
    fi
  fi
  return $?
}

main "$@"
